// util/log_config.go
package util

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// LogFileConfig 日志文件配置（可根据环境调整）
type LogFileConfig struct {
	FileName   string // 日志文件路径（如"logs/app.log"）
	MaxSize    int    // 单文件最大大小（MB）
	MaxBackups int    // 最大备份文件数
	MaxAge     int    // 保留天数
	Compress   bool   // 是否压缩备份文件
}

// 全局日志实例
var Logger *zap.Logger
var SqlLogger *zap.Logger // 单独的SQL日志实例（可选，用于拆分文件）

// 全局日志实例（按日期分割）
var (
	AppLogger *zap.Logger // app.log（请求/响应日志）
)

// InitDailyLog 初始化按日期分割的日志器
func InitDailyLog() {
	// 确保logs目录存在（不存在则创建）
	if err := os.MkdirAll("logs", 0755); err != nil {
		panic(fmt.Sprintf("创建日志目录失败: %v", err))
	}

	// 1. 配置app.log（按日期分割）
	appWriteSyncer := getDailyLogWriter("app") // 前缀为"app"
	appCore := zapcore.NewCore(getEncoder(), appWriteSyncer, zap.InfoLevel)
	AppLogger = zap.New(appCore, zap.AddCaller())

	// 2. 配置sql.log（按日期分割）
	sqlWriteSyncer := getDailyLogWriter("sql") // 前缀为"sql"
	sqlCore := zapcore.NewCore(getEncoder(), sqlWriteSyncer, zap.InfoLevel)
	SqlLogger = zap.New(sqlCore, zap.AddCaller())

	// 3. 启动定时任务：每天凌晨切换日志文件
	startLogRotator()
}

// startLogRotator 启动定时任务，每天凌晨切换日志文件
func startLogRotator() {
	// 计算当前时间到次日凌晨的时间差
	now := time.Now()
	tomorrow := now.Add(24 * time.Hour)
	nextRotate := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 0, 0, 0, 0, tomorrow.Location())
	duration := nextRotate.Sub(now)

	// 首次延迟执行（到次日凌晨）
	time.AfterFunc(duration, func() {
		// 每天凌晨执行一次日志切换
		ticker := time.NewTicker(24 * time.Hour)
		defer ticker.Stop()

		for {
			// 切换日志文件：重新初始化日志器
			rotateLogs()
			<-ticker.C
		}
	})
}

// rotateLogs 切换日志文件（重新初始化日志器）
func rotateLogs() {
	// 关闭旧的日志器（释放文件句柄）
	_ = AppLogger.Sync()
	_ = SqlLogger.Sync()

	// 重新初始化日志器（生成新日期的文件名）
	appWriteSyncer := getDailyLogWriter("app")
	appCore := zapcore.NewCore(getEncoder(), appWriteSyncer, zap.InfoLevel)
	AppLogger = zap.New(appCore, zap.AddCaller())

	sqlWriteSyncer := getDailyLogWriter("sql")
	sqlCore := zapcore.NewCore(getEncoder(), sqlWriteSyncer, zap.InfoLevel)
	SqlLogger = zap.New(sqlCore, zap.AddCaller())

	AppLogger.Info("日志文件已切换至新日期")
}

// getDailyLogWriter 创建按日期分割的日志写入器（结合lumberjack）
func getDailyLogWriter(prefix string) zapcore.WriteSyncer {
	// 动态生成当前日期的文件名
	fileName := getLogFileName(prefix)

	// 配置lumberjack（日志轮转）
	lumberJackLogger := &lumberjack.Logger{
		Filename:   fileName, // 带日期的文件名
		MaxSize:    100,      // 单文件最大100MB（即使没到凌晨，超过大小也会分割）
		MaxBackups: 7,        // 最多保留7个备份文件
		MaxAge:     7,        // 日志保留7天
		Compress:   true,     // 压缩备份文件
		LocalTime:  true,     // 用本地时间命名备份文件
	}

	return zapcore.AddSync(lumberJackLogger)
}

// InitFileLogger 初始化日志（输出到文件，带轮转）
func InitFileLogger() {
	// 1. 配置请求/响应日志文件（app.log）
	appLogConfig := LogFileConfig{
		FileName:   "logs/app.log",
		MaxSize:    100,  // 单个文件100MB
		MaxBackups: 10,   // 最多保留10个备份
		MaxAge:     30,   // 保留7天
		Compress:   true, // 压缩备份文件
	}

	// 2. 配置SQL日志文件（sql.log，可选拆分）
	sqlLogConfig := LogFileConfig{
		FileName:   "logs/sql.log",
		MaxSize:    200, // SQL日志可能更大，设200MB
		MaxBackups: 10,
		MaxAge:     30, // 保留15天便于调试
		Compress:   true,
	}

	// 3. 创建文件写入器（带轮转）
	appWriteSyncer := getLogWriter(appLogConfig)
	sqlWriteSyncer := getLogWriter(sqlLogConfig)

	// 4. 配置Zap编码器（JSON格式，便于解析）
	encoder := getEncoder()

	// 5. 构建日志核心（区分app和sql日志）
	appCore := zapcore.NewCore(encoder, appWriteSyncer, zap.InfoLevel)
	sqlCore := zapcore.NewCore(encoder, sqlWriteSyncer, zap.InfoLevel)

	// 6. 初始化日志实例
	Logger = zap.New(appCore, zap.AddCaller())    // app日志（含调用栈）
	SqlLogger = zap.New(sqlCore, zap.AddCaller()) // SQL日志（单独文件）
}

// getLogWriter 创建带轮转的文件写入器
func getLogWriter(config LogFileConfig) zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   config.FileName,
		MaxSize:    config.MaxSize,
		MaxBackups: config.MaxBackups,
		MaxAge:     config.MaxAge,
		Compress:   config.Compress,
	}
	return zapcore.AddSync(lumberJackLogger)
}

// getEncoder 配置Zap的JSON编码器（结构化日志）
func getEncoder() zapcore.Encoder {
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller", // 调用文件路径
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalLevelEncoder,    // 级别大写（INFO/ERROR）
		EncodeTime:     zapcore.ISO8601TimeEncoder,     // 时间格式：2006-01-02T15:04:05.000Z0700
		EncodeDuration: zapcore.SecondsDurationEncoder, // 耗时单位：秒
		EncodeCaller:   zapcore.ShortCallerEncoder,     // 调用者路径简写（如util/log.go:23）
	}
	return zapcore.NewJSONEncoder(encoderConfig)
}

// 获取带日期的日志文件名（如"logs/app_20231023.log"）
func getLogFileName(prefix string) string {
	// 日期格式：YYYYMMDD（如20231023）
	date := time.Now().Format("20060102")
	// 拼接路径：prefix + _ + 日期 + .log
	return filepath.Join("logs", fmt.Sprintf("%s_%s.log", prefix, date))
}
